﻿using System.Collections;
using System.IO;
using System.Xml;
using Ionic.Zip;

namespace EpubSqueaker
{
	public static class EpubTools
	{
		/// <summary>
		/// Return an iterator of ZipEntries (should generally be just one) of
		/// OPF files.
		/// </summary>
		/// <param name="epub">The epub (as a ZIP file).</param>
		/// <param name="preserveWhiteSpace">Preserve whitespace in the container XML.</param>
		public static IEnumerable OpfFilesInEpub(ZipFile epub)
		{
			// TODO: This fails utterly if the container.xml is XML 1.1 and has a byte-order mark, or is a multibyte encoding.

			foreach (ZipEntry zeContainer in epub.SelectEntries("container.xml", "META-INF"))
			{
				// Unzip the container.xml file.
				var streamContainer = new MemoryStream();
				zeContainer.Extract(streamContainer);
				streamContainer.Seek(0, SeekOrigin.Begin);

				// HACK: Convert from XML 1.1 to XML 1.0 if necessary, since Microsoft's XML tools only support 1.0.
				if (XmlTools.ConvertXml11ToXml10(streamContainer))
				{
					System.Diagnostics.Debug.WriteLine("Updated container " + epub.Name);
				}

				// Open the container.xml (or the de-XML 1.1'd container.xml) for parsing.
				XmlDocument xmlContainer = new XmlDocument();
				xmlContainer.Load(streamContainer);

				/*
				* Parse the container.xml looking for the OPF file:
				* <container>
				*   <rootfiles>
				*     <rootfile media-type="application/oebps-package+xml" full-path="WHAT WE ARE PARSING FOR" />
				*   </rootfiles>
				* </container>
				*/

				// Set up our XML namespace information.
				XmlNamespaceManager nsmgr = new XmlNamespaceManager(xmlContainer.NameTable);
				nsmgr.AddNamespace("container", "urn:oasis:names:tc:opendocument:xmlns:container");

				// Look for the rootfile with the specified media-type.
				foreach (XmlNode rootfile in xmlContainer.SelectNodes("//container:container/container:rootfiles/container:rootfile[@media-type=\"application/oebps-package+xml\"]", nsmgr))
				{
					if ((rootfile is XmlElement) && (((XmlElement)rootfile).HasAttribute("full-path")))
					{
						var opfFile = new FileReference(rootfile.Attributes["full-path"].Value);

						// Theoretically at this point we know where the OPF file is. Let's open 'er up!
						foreach (ZipEntry zeOpf in epub.SelectEntries(opfFile.FileNameForZip, opfFile.DirectoryName))
						{
							yield return zeOpf;
						}
					}
				}
			}
		}
	}
}
